﻿using ServiceStack.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace vueweb
{
	public class RedisClientsManagerWrap : IDisposable
	{
		private SentinelConfig config;
		private RedisSentinel _sentinel;
		private IRedisClientsManager _manager;
		private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);

		public RedisClientsManagerWrap(SentinelConfig sentinelConfig)
		{
			config = sentinelConfig;
		}

		private void Connect()
		{
			if (_sentinel != null)
			{
				if (_manager == null)
				{
					_manager = _sentinel.Start();
				}
				return;
			}
			_connectionLock.Wait();

			try
			{
				if (_sentinel == null)
				{
					// sentinel settings
					_sentinel = new RedisSentinel(config.Hosts, config.MasterGroup)
					{
						ScanForOtherSentinels = config.ScanForOtherSentinels,
						RedisManagerFactory = (master, slaves) => new RedisManagerPool(master),
						// set the client connection string
						HostFilter = host =>
						{
							var h = String.Empty;
							h += String.IsNullOrWhiteSpace(config.AuthPass) ? "" : $"{config.AuthPass}@";
							h += $"{host}?db={config.DefaultDatabase}&RetryTimeout=5000";
							return h;
						},
					};

					// start monitoring
					_manager = _sentinel.Start();
				}

			}
			catch (Exception ex)
			{
				Console.WriteLine(ex);
			}
			finally
			{
				_connectionLock.Release();
			}

		}

		private async Task ConnectAsync(CancellationToken token = default(CancellationToken))
		{
			token.ThrowIfCancellationRequested();

			if (_sentinel != null)
			{
				if (_manager == null)
				{
					_manager = await Task.Run(() => _sentinel.Start()).ConfigureAwait(false);
				}
				return;
			}
			await _connectionLock.WaitAsync(token);

			try
			{
				if (_sentinel == null)
				{
					// sentinel settings
					_sentinel = new RedisSentinel(config.Hosts, config.MasterGroup)
					{
						ScanForOtherSentinels = config.ScanForOtherSentinels,
						RedisManagerFactory = (master, slaves) => new RedisManagerPool(master),
						// set the client connection string
						HostFilter = host => {
							var h = String.Empty;
							h += String.IsNullOrWhiteSpace(config.AuthPass) ? "" : $"{config.AuthPass}@";
							h += $"{host}?db={config.DefaultDatabase}&RetryTimeout=5000";
							return h;
						},
					};

					// start monitoring
					_manager = await Task.Run(() => _sentinel.Start()).ConfigureAwait(false);
				}
			}
			finally
			{
				_connectionLock.Release();
			}
		}

		public IRedisClientsManager Get()
		{
			Connect();
			return _manager;
		}

		public async Task<IRedisClientsManager> GetAsync(CancellationToken token = default(CancellationToken))
		{
			token.ThrowIfCancellationRequested();
			await ConnectAsync(token);
			return _manager;
		}

		public void Dispose()
		{
			_manager?.Dispose();
			_sentinel?.Dispose();
		}
	}
}
